package decodingSimulaiton;

import java.util.ArrayList;
import java.util.BitSet;

public class DecodingImplementationVer2 {
	private int k;
	private int n;
	int[][] G;
	int[][] I;
	int[] serverInserted;
	int numOnes[];

	public DecodingImplementationVer2(int k, int n) {
		super();
		this.k = k;
		this.n = n;
		G = new int[k][k];
		I = new int[k][k];
		numOnes = new int[k];
		serverInserted = new int[k];
		for (int i = 0; i < k; i++) {
			for (int j = 0; j < k; j++) {
				/**
				 *  G is the OFG matrix created in this code 
				 *  b is the generator matrix passed
				 */
				G[i][j] = 0;
				I[i][j] = 0;
			}
		}
	}
	
	/**********************************************************
	 * OFG method to find the initial set of linearly independent servers 
	 *********************************************************/
	public int[] onTheFlyServer(int[][] b, int[][] identityMatrix) {
		int currServer = 0;
		int emptyRows = k - 1;
		int[] eqn = new int[k];
		int eqOnes = 0;
		int leftMost = 0;
		int s = 0;
		int i = -1;
		emptyRows = k;
		BitSet tempBitSet;
		/*
		 * array to keep track of which server index is finally actually
		 * inserted at which position within the G matrix
		 */

		i = 0;
		while (emptyRows > 0 && i <= n - 2) {
			currServer = i;
			eqOnes = 0;
			leftMost = 0;
			s = 0;
			/*
			 * eqn = extract one row from the generator matrix 
			 */
			for (int j = 0; j < k; j++) {
				eqn[j] = b[i][j];

				if (eqn[j] == 1) {
					eqOnes++;
					if (leftMost == 0) {
						leftMost = 1;
						s = j;
					}
				}
			}
			while (G[s][s] == 1 && eqOnes > 0) {
				if (eqOnes >= numOnes[s]) {
					/*
					 * eqn'=xor G[s] and eqn
					 */
					for (int l = 0; l < k; l++) {
						eqn[l] = (eqn[l] + G[s][l]) % 2;
					}
				} else {
					/*
					 * swap eqn and G[s]
					 */
					for (int l = 0; l < k; l++) {
						int temp = eqn[l];
						eqn[l] = G[s][l];
						G[s][l] = temp;
					}
					numOnes[s] = eqOnes;

				}
				/*
				 * calculate left most 1 in eqn and degree of eqn'( numOfOnes)
				 */
				leftMost = 0;
				s = 0;
				eqOnes = 0;
				int l = 0;
				while (l < k) {
					if (eqn[l] == 1) {
						if (leftMost == 0) {
							s = l;
							leftMost++;
						}
						eqOnes++;
					}
					l++;
				}
			}
			if (eqOnes > 0) {

				/*
				 * add eqn to G[s], gDegree=numOfOnes, emptyRows=emptyRows-1
				 */
				for (int m = 0; m < k; m++) {
					G[s][m] = eqn[m];
				}
				//InitialServerSet.encodedFragments.set(s, tempBitSet);
				numOnes[s] = eqOnes;
				emptyRows = emptyRows - 1;
				serverInserted[s] = currServer;
			} else {
			}
			/*
			 * discard equation if has all 0s or all 1s
			 */
			i++;
		}
		return serverInserted;
	}
	
	/**********************************************************
	 * OFG method where the OFG matrix is deleted and set the row in G matrix to 0
	 *********************************************************/
	

	public int[] onNextSetOfServerVer2(int[][] b, int serverNumber,
			int[][] identity) {		
		int emptyRows = k;
		int currServer;
		int[] eqn = new int[k];		
		int eqOnes = 0;
		int leftMost = 0;
		int s = 0;
//		I = identity.clone();
		for (int j = 0; j < k; j++) {
			/*
			 * In matrix b / transpose the serverNumber is equivalent to the row
			 * number but in matrix G find the index correspoding to the server
			 * number and set that row to 0
			 */
			b[serverNumber][j] = 0;
		}

	
		//InitialServerSet.encodedFragments.get(serverNumber).clear();
		/*
		 * entire G matrix is filled with zeros
		 */
		for (int i = 0; i < k; i++) {
			serverInserted[i] = 0;
			numOnes[i] = 0;
			for (int j = 0; j < k; j++) {
				G[i][j] = 0;
			}
		}
		int i = 0;
		while (emptyRows > 0 && i <= n - 2) {
			currServer = i;
			eqOnes = 0;
			leftMost = 0;
			s = 0;
			/*
			 * eqn = extract one row from the generator matrix 
			 */
			for (int j = 0; j < k; j++) {
				eqn[j] = b[i][j];

				if (eqn[j] == 1) {
					eqOnes++;
					if (leftMost == 0) {
						leftMost = 1;
						s = j;
					}
				}
			}
			while (G[s][s] == 1 && eqOnes > 0) {
				if (eqOnes >= numOnes[s]) {
					/*
					 * eqn'=xor G[s] and eqn
					 */
					for (int l = 0; l < k; l++) {
						eqn[l] = (eqn[l] + G[s][l]) % 2;
					}
				} else {
					/*
					 * swap eqn and G[s]
					 */
					for (int l = 0; l < k; l++) {
						int temp = eqn[l];
						eqn[l] = G[s][l];
						G[s][l] = temp;
					}
					numOnes[s] = eqOnes;

				}
				/*
				 * calculate left most 1 in eqn and degree of eqn'( numOfOnes)
				 */
				leftMost = 0;
				s = 0;
				eqOnes = 0;
				int l = 0;
				while (l < k) {
					if (eqn[l] == 1) {
						if (leftMost == 0) {
							s = l;
							leftMost++;
						}
						eqOnes++;
					}
					l++;
				}
			}
			if (eqOnes > 0) {

				/*
				 * add eqn to G[s], gDegree=numOfOnes, emptyRows=emptyRows-1
				 */
				for (int m = 0; m < k; m++) {
					G[s][m] = eqn[m];
				}
				//InitialServerSet.encodedFragments.set(s, tempBitSet);
				numOnes[s] = eqOnes;
				emptyRows = emptyRows - 1;
				serverInserted[s] = currServer;
			} else {
			}
			/*
			 * discard equation if has all 0s or all 1s
			 */
			i++;
		}
		System.out.println();
		for (int j : serverInserted) {
			System.out.println(j);
		}

		return serverInserted;
	}
	
	
	public ArrayList<BitSet> decodePacketsVer2(ArrayList<BitSet> encodedFragments, int[][] b, int k, int N_new,
			int[] sourceIndex) {
		// ArrayList<BitSet> ec = encodedFragments;
		// System.out.println(" original matrix");
		// printMatrix(G, k, N_new);
		// this.encodedFragments= encodedFragments;
		ArrayList<BitSet> encodedFragmentsNew = new ArrayList<BitSet>();
		int[][] newGenerator = new int[k][k];
		int l = 0;
		for (int i : sourceIndex) {
			for (int j = 0; j < k; j++) {
				newGenerator[l][j] = b[i][j];
			}
			encodedFragmentsNew
					.add(l, (BitSet) encodedFragments.get(i).clone());
			l++;
		}
		// printMatrix(newGenerator, k, k);

		int n = k;
		int N = k;
		// ArrayList<String> decodedString = new ArrayList<String>();
		for (int p = 0; p < n; p++) {
			int max = p;
			for (int i = p + 1; i < N; i++) {
				if (Math.abs(newGenerator[i][p]) > Math
						.abs(newGenerator[max][p])) {
					max = i;
				}
			}
			int[] temp = newGenerator[p];
			newGenerator[p] = newGenerator[max];
			newGenerator[max] = temp;

			BitSet t = encodedFragmentsNew.get(p);
			encodedFragmentsNew.set(p, encodedFragmentsNew.get(max));
			encodedFragmentsNew.set(max, t);
			t = null;
			// System.out.println(" diagonal matrix");
			// printMatrix(G, k, N_new);

			if (Math.abs(newGenerator[p][p]) != 1) {
				throw new RuntimeException(
						"Matrix is singular or nearly singular");
			}

			for (int i = p + 1; i < N; i++) {
				int alpha = -(newGenerator[i][p] / newGenerator[p][p]);
				int index = i;
				BitSet value = encodedFragmentsNew.get(index);
				if (alpha == 0) {
					/*
					 * change all elements of this.encodedFragments.get(p)=0
					 */
					// this.encodedFragments.get(p).clear();
					encodedFragmentsNew.set(index, value);
				} else {
					// e= e+ a*G
					value.xor(encodedFragmentsNew.get(p));
					encodedFragmentsNew.set(index, value);
				}
				value = null;
				for (int j = p; j < n; j++) {
					newGenerator[i][j] = newGenerator[i][j]
							^ (alpha & newGenerator[p][j]);
				}
			}		
			for (int i = 0; i <= p - 1; i++) {
				int alpha = -(newGenerator[i][p] / newGenerator[p][p]);
				int index = i;
				BitSet value = encodedFragmentsNew.get(index);
				if (alpha == 0) {
					/*
					 * change all elements of this.encodedFragments.get(p)=0
					 */
					// this.encodedFragments.get(p).clear();
					// this.encodedFragments.remove(p);
					encodedFragmentsNew.set(index, value);
				} else {
					value.xor(encodedFragmentsNew.get(p));
					encodedFragmentsNew.set(index, value);
				}
				value = null;
				for (int j = p; j < n; j++) {
					newGenerator[i][j] = newGenerator[i][j]
							^ (alpha & newGenerator[p][j]);
				}
			}
			// System.out.println(" upper triangle");
			// printMatrix(newGenerator, k, N_new);
			// System.out
			// .println("variable number is +++++++++++++++++++++++++++++++++++"
			// + p);
			// System.out.println(new String(encodedFragmentsNew.get(p)
			// .toByteArray()));
		}

		ArrayList<BitSet> obtainedSource = new ArrayList<BitSet>();
		for (int i = 0; i < n; i++) {
			if (newGenerator[i][i] == 0) {
				System.out.println(" error in the matrix cannot solve");
			} else {
				/*
				 * assign the values of the of the current encoded fragments to
				 * obtained source *
				 */
				obtainedSource.add(encodedFragmentsNew.get(i));
			}
		}
		return obtainedSource;
	}

}
